/* Copyright (c) 2006 celumsolutions. All Rights Reserved. */
package com.swingolf.web;

import com.google.appengine.api.images.ImagesService;
import com.google.appengine.api.images.ImagesServiceFactory;
import com.google.appengine.api.images.OutputSettings;
import com.google.appengine.api.images.Transform;
import com.swingolf.dao.TournamentDAO;
import com.swingolf.dao.hrd.HRDGuiStatusDAOImpl;
import com.swingolf.dao.hrd.HRDImageDAOImpl;
import com.swingolf.dao.hrd.HRDTeamDAOImpl;
import com.swingolf.model.Player;
import com.swingolf.model.Players;
import com.swingolf.model.Rank;
import com.swingolf.model.Team;
import com.swingolf.model.Teams;
import com.swingolf.model.Tournament;
import com.swingolf.model.Tournaments;
import com.swingolf.service.PlayerService;
import com.swingolf.service.RankService;
import org.apache.commons.fileupload.FileItemIterator;
import org.apache.commons.fileupload.FileItemStream;
import org.apache.commons.fileupload.FileUpload;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.ServletRequestUtils;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.*;
import javax.validation.*;
import java.io.*;
import java.text.*;
import java.util.*;
import java.util.logging.*;

/**
 * @author rtm
 * @since 13.09.11
 */
@Controller
public class LiveStatusController {

  private static final Logger log =
      Logger.getLogger(FileUpload.class.getName());
  @Autowired
  RankService rankService;

  @Autowired
  PlayerService playerService;

  @Autowired
  Validator validator;

  @Autowired
  HRDImageDAOImpl imageDAO;

  @Autowired
  HRDTeamDAOImpl teamDAO;

  @Autowired
  TournamentDAO tournamentDAO;

  @Autowired
  HRDGuiStatusDAOImpl guiStatusDAO;

  @RequestMapping("/index.html")
  public ModelAndView index() {
    Tournament scramble = null;
    List<Tournament> tournaments = tournamentDAO.findAll();
    for (Tournament tournament : tournaments) {
      if (tournament.getName().equalsIgnoreCase("solo")) {
        scramble = tournament;
      }
    }
    if (scramble == null) {
      return new ModelAndView("index-solo");
    }

    Map model = new HashMap();
    //model.put("ranks", rankService.getRanks(scramble));
    List<Player> players = playerService.findPlayers();
    model.put("ranksHofm", rankService.getRanks(players, com.swingolf.model.Level.HOFM, scramble));
    model.put("ranksHofw", rankService.getRanks(players, com.swingolf.model.Level.HOFW, scramble));
    model.put("ranksJugm", rankService.getRanks(players, com.swingolf.model.Level.JUGM, scramble));
    model.put("ranksJugw", rankService.getRanks(players, com.swingolf.model.Level.JUGW, scramble));
    model.put("ranksSH1", rankService.getRanks(players, com.swingolf.model.Level.SH1, scramble));
    model.put("ranksSH2", rankService.getRanks(players, com.swingolf.model.Level.SH2, scramble));
    model.put("ranksSH3", rankService.getRanks(players, com.swingolf.model.Level.SH3, scramble));
    model.put("ranksSF1", rankService.getRanks(players, com.swingolf.model.Level.SF1, scramble));
    model.put("ranksSF2", rankService.getRanks(players, com.swingolf.model.Level.SF2, scramble));
    model.put("ranksSF3", rankService.getRanks(players, com.swingolf.model.Level.SF3, scramble));
    //model.put("firstRank", rankService.getFirstRank(scramble));
    SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    df.setTimeZone(TimeZone.getTimeZone("Europe/Vienna"));
    model.put("date", df.format(new Date()));
    return new ModelAndView("index", model);
  }

  @RequestMapping("/index-scramble.html")
  public ModelAndView indexScramble() {
    Tournament scramble = null;
    List<Tournament> tournaments = tournamentDAO.findAll();
    for (Tournament tournament : tournaments) {
      if (tournament.getName().equalsIgnoreCase("scramble")) {
        scramble = tournament;
      }
    }
    if (scramble == null) {
      return new ModelAndView("index-scramble");
    }
    Map model = new HashMap();
    Collection<Rank> ranks = rankService.getRanks(scramble);
    model.put("ranks5", sub(new LinkedList(ranks), 0, 5));
    model.put("ranks5_15", sub(new LinkedList(ranks), 5, 15));
    model.put("ranks15_25", sub(new LinkedList(ranks), 15, 25));
    SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    df.setTimeZone(TimeZone.getTimeZone("Europe/Vienna"));
    model.put("date", df.format(new Date()));
    return new ModelAndView("index-scramble", model);
  }

  private Collection<Rank> sub(List<Rank> ranks, int from, int to) {
    return ranks.size()>from ? ranks.subList(from, to>ranks.size() ? ranks.size() : to) : new LinkedList<Rank>();
  }

  @RequestMapping("/edit.html")
  public void editPlayers(Map model) {
    model.put("player", new Player());
    List<Player> players = playerService.findPlayers();
    model.put("players", new Players(players));
    int i = 0;
    for (Player player : players) {
      model.put("player[" + i++ + "]", player);
    }
    LinkedList<Team> teams = new LinkedList<Team>();
    Team noselection = new Team();
    noselection.setName("Keine Auswahl");
    teams.add(noselection);
    teams.addAll(teamDAO.findAll());
    model.put("teams", teams);

    String view = guiStatusDAO.find("view");
    if (view != null) {
      model.put("view", new EditView(EditViewType.valueOf(view)));
    } else {
      model.put("view", new EditView());
    }
    model.put("views", EditViewType.values());

    String tournamentId = guiStatusDAO.find("tournament");
    if (tournamentId != null) {
      model.put("tournament", new TournamentHolder(tournamentDAO.findById(Long.parseLong(tournamentId))));
    } else {
      model.put("tournament", new TournamentHolder(null));
    }

    model.put("levels", com.swingolf.model.Level.values());
    LinkedList<Tournament> tournaments = new LinkedList<Tournament>();
    Tournament noselectionTournament = new Tournament();
    noselectionTournament.setName("Keine Auswahl");
    tournaments.add(noselectionTournament);
    tournaments.addAll(tournamentDAO.findAll());
    model.put("tournaments", tournaments);


  }

  @RequestMapping(value = "/updateeditview.html", method = RequestMethod.POST)
  public String saveEditView(@ModelAttribute("view") @Valid EditView editView) {
    guiStatusDAO.save("view", editView.getEditView().toString());
    return "redirect:/edit.html";
  }

  @RequestMapping(value = "/updateedittournament.html", method = RequestMethod.POST)
  public String saveEditTournament(@ModelAttribute("tournament") @Valid TournamentHolder tournamentHolder) {
    if (tournamentHolder.getTournament() == null) {
      guiStatusDAO.save("tournament", null);
      return "redirect:/edit.html";
    }
    guiStatusDAO.save("tournament", tournamentHolder.getTournament().getId().toString());
    return "redirect:/edit.html";
  }


  @RequestMapping(value = "/save.html", method = RequestMethod.POST)
  public String save(@ModelAttribute("player") @Valid Player player) {
    playerService.savePlayer(player);
    return "redirect:/edit.html";
  }

  @RequestMapping(value = "/update.html", method = RequestMethod.POST)
  public String update(@ModelAttribute("player") @Valid Player player) {
    playerService.updatePlayer(player);
    return "redirect:/edit.html";
  }

  @RequestMapping(value = "/delete.html", method = RequestMethod.POST)
  public String delete(@RequestParam("id") Long id, @ModelAttribute("players") Players players) {
    for (Player player : playerService.findPlayers()) {
      if (player.getId().equals(id)) {
        playerService.deletePlayer(player);
      }
    }
    return "redirect:/edit.html";
  }

  @RequestMapping(value = "/deleteimage.html", method = RequestMethod.POST)
  public String deleteimage(@RequestParam("id") Long id, @ModelAttribute("players") Players players) {
    for (Player player : playerService.findPlayers()) {
      if (player.getId().equals(id)) {
        player.setImageId(null);
        playerService.updatePlayer(player);
      }
    }
    return "redirect:/edit.html";
  }

  @RequestMapping(value = "/showImage.html", method = RequestMethod.GET)
  public void showImage(@RequestParam("id") Long id, @RequestParam(value = "dimension") Integer dimension, HttpServletRequest req, HttpServletResponse res) throws IOException {
    if (dimension == null) {
      dimension = 400;
    }
    byte[] decodedImage = null;
    if (id != null) {
      com.swingolf.model.Image image = imageDAO.findById(id);
      decodedImage = image.getImage();
    } else {
      decodedImage = FileUtils.readFileToByteArray(new File(req.getRealPath("WEB-INF/images/noimage.jpg")));
    }

    byte[] resizedImage = resize(decodedImage, dimension);
    res.setContentType("image/jpeg");
    res.setDateHeader("Expires", System.currentTimeMillis() + 5*60*1000);
    res.setContentLength(resizedImage.length);
    res.getOutputStream().write(resizedImage);
    res.getOutputStream().flush();
  }

  @RequestMapping(value = "/upload.html", method = RequestMethod.POST)
  public String upload(HttpServletRequest req, HttpServletResponse res) {
    try {
      Long id = ServletRequestUtils.getLongParameter(req, "id");
      ServletFileUpload upload = new ServletFileUpload();
      res.setContentType("text/plain");

      FileItemIterator iterator = upload.getItemIterator(req);
      while (iterator.hasNext()) {
        FileItemStream item = iterator.next();
        InputStream stream = item.openStream();

        if (item.isFormField()) {
          log.warning("Got a form field: " + item.getFieldName());
        } else {
          log.warning("Got an uploaded file: " + item.getFieldName() +
                          ", name = " + item.getName());

          // You now have the filename (item.getName() and the
          // contents (which you can read from stream). Here we just
          // print them back out to the servlet output stream, but you
          // will probably want to do something more interesting (for
          // example, wrap them in a Blob and commit them to the
          // datastore).
          int len;

          byte[] image = IOUtils.toByteArray(stream);
          Player player = playerService.findPlayer(id);
          image = resize(image);
          com.swingolf.model.Image img = new com.swingolf.model.Image(image);
          imageDAO.save(img);
          player.setImageId(img.getId());
          playerService.updatePlayer(player);
          //while ((len = stream.read(buffer, 0, buffer.length)) != -1) {
          //res.getOutputStream().write(buffer, 0, len);
          //}
        }
      }
    } catch (Exception ex) {
      throw new RuntimeException(ex);
    }
    return "redirect:/edit.html";
  }

  private byte[] resize(byte[] image) {
    return resize(image, 400);
  }

  private byte[] resize(byte[] image, int dimension) {
    ImagesService imagesService = ImagesServiceFactory.getImagesService();

    com.google.appengine.api.images.Image oldImage = ImagesServiceFactory.makeImage(image);
    Transform resize = ImagesServiceFactory.makeResize(dimension, dimension);
    OutputSettings os = new OutputSettings(ImagesService.OutputEncoding.JPEG);
    os.setQuality(100);
    com.google.appengine.api.images.Image newImage = imagesService.applyTransform(resize, oldImage, os);

    byte[] newImageData = newImage.getImageData();
    return newImageData;
  }

  //private String compress(String input) {
  //ByteArrayOutputStream baos = new ByteArrayOutputStream();
  //try {
  //  System.out.println(input.length());
  //  CompressorOutputStream cos = new CompressorStreamFactory().
  //      createCompressorOutputStream(CompressorStreamFactory.GZIP, baos);
  //  IOUtils.copy(new ByteArrayInputStream(input.getBytes()), cos);
  //  String result = new String(baos.toByteArray());
  //  System.out.println(result.length());
  //  return result;
  //} catch (CompressorException e) {
  //  throw new RuntimeException(e.getMessage(), e);
  //} catch (IOException e) {
  //  throw new RuntimeException(e.getMessage(), e);
  //}
  //}

  @RequestMapping("/editteam.html")
  public void editTeams(Map model) {
    model.put("team", new Team());
    List<Team> teams = teamDAO.findAll();
    model.put("teams", new Teams(teams));
    int i = 0;
    for (Team team : teams) {
      model.put("team[" + i++ + "]", team);
    }
  }

  @RequestMapping(value = "/saveteam.html", method = RequestMethod.POST)
  public String save(@ModelAttribute("team") @Valid Team team) {
    teamDAO.save(team);
    return "redirect:/editteam.html";
  }

  @RequestMapping(value = "/updateteam.html", method = RequestMethod.POST)
  public String updateTeam(@RequestParam("id") Long id, @ModelAttribute("team") @Valid Team team) {
    team.setPlayers(teamDAO.findById(id).getPlayers());
    teamDAO.update(team);
    return "redirect:/editteam.html";
  }


  @RequestMapping(value = "/uploadcountry.html", method = RequestMethod.POST)
  public String uploadTeam(HttpServletRequest req, HttpServletResponse res) {
    try {
      Long id = ServletRequestUtils.getLongParameter(req, "id");
      ServletFileUpload upload = new ServletFileUpload();
      res.setContentType("text/plain");

      FileItemIterator iterator = upload.getItemIterator(req);
      while (iterator.hasNext()) {
        FileItemStream item = iterator.next();
        InputStream stream = item.openStream();

        if (item.isFormField()) {
          log.warning("Got a form field: " + item.getFieldName());
        } else {
          log.warning("Got an uploaded file: " + item.getFieldName() +
                          ", name = " + item.getName());

          // You now have the filename (item.getName() and the
          // contents (which you can read from stream). Here we just
          // print them back out to the servlet output stream, but you
          // will probably want to do something more interesting (for
          // example, wrap them in a Blob and commit them to the
          // datastore).
          int len;

          byte[] image = IOUtils.toByteArray(stream);
          Team team = teamDAO.findById(id);
          image = resize(image);
          com.swingolf.model.Image img = new com.swingolf.model.Image(image);
          imageDAO.save(img);
          team.setCountryImageId(img.getId());
          teamDAO.update(team);
          //while ((len = stream.read(buffer, 0, buffer.length)) != -1) {
          //res.getOutputStream().write(buffer, 0, len);
          //}
        }
      }
    } catch (Exception ex) {
      throw new RuntimeException(ex);
    }
    return "redirect:/editteam.html";
  }

  @RequestMapping(value = "/deleteteam.html", method = RequestMethod.POST)
  public String deleteTeam(@RequestParam("id") Long id, @ModelAttribute("teams") Teams teams) {
    for (Team team : teamDAO.findAll()) {
      if (team.getId().equals(id)) {
        teamDAO.delete(team);
      }
    }
    return "redirect:/editteam.html";
  }

  @RequestMapping("/edittournament.html")
  public void editTournaments(Map model) {
    model.put("tournament", new Tournament());
    List<Tournament> tournaments = tournamentDAO.findAll();
    model.put("tournaments", new Tournaments(tournaments));
    int i = 0;
    for (Tournament tournament : tournaments) {
      model.put("tournament[" + i++ + "]", tournament);
    }
  }

  @RequestMapping(value = "/savetournament.html", method = RequestMethod.POST)
  public String save(@ModelAttribute("tournament") @Valid Tournament tournament) {
    tournamentDAO.save(tournament);
    return "redirect:/edittournament.html";
  }

  @RequestMapping(value = "/updatetournament.html", method = RequestMethod.POST)
  public String updateTournament(@RequestParam("id") Long id, @ModelAttribute("tournament") @Valid Tournament tournament) {
    tournamentDAO.update(tournament);
    return "redirect:/edittournament.html";
  }

  @RequestMapping(value = "/deletetournament.html", method = RequestMethod.POST)
  public String deleteTournament(@RequestParam("id") Long id, @ModelAttribute("tournaments") Tournaments tournaments) {
    for (Tournament tournament : tournamentDAO.findAll()) {
      if (tournament.getId().equals(id)) {
        tournamentDAO.delete(tournament);
      }
    }
    return "redirect:/edittournament.html";
  }

}
